//------------------------------------------------------------------------------
// RegFilter.cpp
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// Registry IFilter implementation for the registry indexer
//
//------------------------------------------------------------------------------

#include "pch.h"
#include <RegNamespace.h>
#include "RegFilter.h"

//------------------------------------------------------------------------------
// filter handler for a key or value in the registry
//------------------------------------------------------------------------------
CRegFilter::~CRegFilter() 
{    
}

HRESULT CRegFilter::s_CreateInstance(IShellItem *psi, REFIID riid, __deref_out void **ppv)
{     
    *ppv = NULL;
    CComObjectWithRef<CRegFilter> *prf;
    HRESULT hr = CComObjectWithRef<CRegFilter>::CreateInstance(&prf);
    if (SUCCEEDED(hr))
    {
        hr = prf->_Initialize(psi);
        if (SUCCEEDED(hr))
        {
            hr = prf->QueryInterface(riid, ppv);             
        }
        prf->Release();
    }
    return hr;
}

HRESULT CRegFilter::_Initialize(IShellItem *psi)
{    
    _spsi = psi;    
        
    _fs = FS_PROPERTIES;
    _cProps = 0;    
       
    CComPtr<IShellItem2> spsi2;    
    HRESULT hr = _spsi->QueryInterface(IID_PPV_ARGS(&spsi2));
    if (SUCCEEDED(hr))
    {
        hr = _spsi->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&_spenumsi));
        if (SUCCEEDED(hr))
        {
            hr = spsi2->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&_spps));                
        }
    }
    return hr;
}

STDMETHODIMP CRegFilter::Init(ULONG grfFlags, ULONG cAttributes, __deref_in FULLPROPSPEC const *aAttributes, __out ULONG *pFlags)
{
    _fs = FS_PROPERTIES;
    _cProps = 0;    

    return S_OK;
}

STDMETHODIMP CRegFilter::GetChunk(__deref_out STAT_CHUNK *pStat)
{
    typedef struct tagPROPERTY_TABLE
    {
        PROPERTYKEY pkey;
        BOOL fReturnasContents;
    } PROPERTY_TABLE;

    const PROPERTY_TABLE c_ptRegProps[] =
    {
        {PKEY_ItemNameDisplay, FALSE},
        {PKEY_ItemPathDisplay, FALSE},
        {PKEY_ParsingName, FALSE},
        {PKEY_RegistryType, FALSE},
        {PKEY_RegistryTypeDisplay, FALSE},
        {PKEY_RegistryTypeDisplay, TRUE},
        {PKEY_RegistryData, FALSE},
        {PKEY_RegistryData, TRUE},
        {PKEY_DateModified, FALSE},
    };

    BOOL fHaveValue = FALSE;    
    HRESULT hr = _spropvarValue.Clear();
    
    if (SUCCEEDED(hr))
    {
        do
        {
            switch(_fs)
            {
            case FS_PROPERTIES: 
                if (ARRAYSIZE(c_ptRegProps) > _cProps)
                {
                    PROPERTYKEY pkey = c_ptRegProps[_cProps].pkey;            
                    hr = _spps->GetValue(pkey, &_spropvarValue);
                    if (SUCCEEDED(hr))
                    {
                        // Check to see if there is data and if it is in a form the indexer recognizes
                        if (VT_NULL != _spropvarValue.vt && VT_BLOB != _spropvarValue.vt)
                        {
                            // Then pkey is of type that needs to be returned
                            if (c_ptRegProps[_cProps].fReturnasContents)
                            {
                                PROPERTYKEY pkeyContents = PKEY_Search_Contents;
                                pStat->attribute.guidPropSet = pkeyContents.fmtid;                                    
                                pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
                                pStat->attribute.psProperty.propid = pkeyContents.pid;
                            }
                            else
                            {
                                pStat->attribute.guidPropSet = pkey.fmtid;                                    
                                pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
                                pStat->attribute.psProperty.propid = pkey.pid;
                            }
                            fHaveValue = TRUE;
                        }     
                        else
                        {                            
                            hr = _spropvarValue.Clear();
                        }
                    }                    
                    _cProps++;
                }                
                else
                {                    
                    // See if this is a key (directory) or a value (file)
                    DWORD ffAttributes = 0;
                    hr = _spsi->GetAttributes(SFGAO_FOLDER, &ffAttributes);
                    if (SUCCEEDED(hr))
                    {
                        if (ffAttributes & SFGAO_FOLDER)
                        {
                            // It's a key (directory) so now we return the children
                            _fs = FS_CHILDREN;
                        }
                        else
                        {
                            // It's a file so we are done
                            _fs = FS_END;
                        }
                    }
                }                                              
                break;

            case FS_CHILDREN:       
                {
                    CComPtr<IShellItem> spsiCurrChild;
                    hr = _spenumsi->Next(1, &spsiCurrChild, NULL);
                    if (SUCCEEDED(hr))
                    {
                        if (S_OK == hr)
                        {                            
                            CComMemPtr<WCHAR> spszVal; 
                            hr = spsiCurrChild->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &spszVal);
                            if (SUCCEEDED(hr))
                            {
                                CComMemPtr<CComPropVariant> spVector;
                                spVector.Attach((CComPropVariant *)(CoTaskMemAlloc(sizeof(PROPVARIANT) * 2)));
                                hr = spVector ? S_OK : E_OUTOFMEMORY;
                                if (SUCCEEDED(hr))
                                {
                                    _spropvarValue.vt = VT_VARIANT | VT_VECTOR;
                                    _spropvarValue.capropvar.cElems = 2;
                                    _spropvarValue.capropvar.pElems = spVector.Detach();

                                    _spropvarValue.capropvar.pElems[0].vt = VT_LPWSTR;
                                    _spropvarValue.capropvar.pElems[0].pwszVal = spszVal.Detach();

                                    _spropvarValue.capropvar.pElems[1].vt = VT_FILETIME;
                                    GetSystemTimeAsFileTime(&_spropvarValue.capropvar.pElems[1].filetime);

                                    pStat->attribute.guidPropSet = PKEY_Search_UrlToIndexWithModificationTime.fmtid;
                                    pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
                                    pStat->attribute.psProperty.propid = PKEY_Search_UrlToIndexWithModificationTime.pid;
                                    fHaveValue = TRUE;
                                }
                            }                            
                        }
                        else
                        {
                            _fs = FS_END;
                        }
                    }
                }
                break;

            case FS_END:
                hr = FILTER_E_END_OF_CHUNKS;
                break;

            default:
                hr = E_UNEXPECTED;
                break;
            }
        } while (SUCCEEDED(hr) && !fHaveValue);

        if (SUCCEEDED(hr))
        {
            pStat->idChunk = ++_ulChunkId;
            pStat->breakType = CHUNK_EOS;
            pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
            pStat->flags = CHUNK_VALUE;
            pStat->locale = _lcid;                
            pStat->idChunkSource = 0;
            pStat->cwcStartSource = 0;
            pStat->cwcLenSource = 0;
        }
    }
    return hr;
}

STDMETHODIMP CRegFilter::GetText(__inout ULONG *pcwcBuffer, __out_ecount(*pcwcBuffer) WCHAR *awcBuffer)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegFilter::GetValue(__deref_out PROPVARIANT **ppPropValue)
{   
    CComMemPtr<PROPVARIANT> sprop;
    HRESULT hr = sprop.Allocate();    
    if (SUCCEEDED(hr))
    {
        PropVariantInit(sprop);
        hr = _spropvarValue.Detach(sprop);
        if (SUCCEEDED(hr))
        {
            *ppPropValue = sprop.Detach();  
        }
    }    
    return hr;
}

STDMETHODIMP CRegFilter::BindRegion(FILTERREGION origPos, REFIID riid, __deref_out void **ppunk)
{
    return E_NOTIMPL;
}
